﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Phoenix.Framework;
using Phoenix.Utilities;

namespace Phoenix.Frames
{
    public class PartialView : ContentControl, IContentFrameControl
    {
        readonly IContentFrameController contentFrameController;
        Action<bool> setProgressIndicatorVisible;
        ProgressBar progressIndicator;
        bool hasLoaded;

#if !WINDOWS_PHONE
        static PartialView()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(PartialView), new FrameworkPropertyMetadata(typeof(PartialView)));
        }
#endif

        public PartialView()
        {
#if WINDOWS_PHONE
            DefaultStyleKey = typeof(PartialView);
#endif
            Loaded += PartialViewLoaded;
            Unloaded += OnUnloaded;
            HorizontalAlignment = HorizontalAlignment.Stretch;
            VerticalAlignment = VerticalAlignment.Stretch;
            HorizontalContentAlignment = HorizontalAlignment.Stretch;
            VerticalContentAlignment = VerticalAlignment.Stretch;

            // ReSharper disable DoNotCallOverridableMethodsInConstructor
            contentFrameController = CreateContentFrameController();
            // ReSharper restore DoNotCallOverridableMethodsInConstructor
        }

        void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
        {
            hasLoaded = false;
        }

        protected virtual IContentFrameController CreateContentFrameController()
        {
            return new ContentFrameController(this);
        }

        public void ActivateCurrentRequest()
        {
            contentFrameController.ActivateCurrentRequest();
        }

        public void InitialiseHost(IPhoenixHost host)
        {
            contentFrameController.InitialiseHost(host);
        }

        void PartialViewLoaded(object sender, RoutedEventArgs e)
        {
            hasLoaded = true;
            Loaded -= PartialViewLoaded;
            var controllerAction = ControllerAction;

            if (controllerAction == null)
            {
                var viewModel = GetViewModel();
                //If there is no viewmodel, have to assume that we are the root frame
                if (viewModel != null)
                {
                    //If we have no initial action, we need to register ourselves anyway so we can be targeted by name
                    viewModel.Context.ContentFrame.RegisterChild(this);
                }
                else if (contentFrameController.Host != null)
                {
                    contentFrameController.Host.HostTarget.NavigationFrame.RegisterChild(this);
                }
            }
            else
            {
                InvokeControllerAction(controllerAction);
            }
        }

        ViewModelBase GetViewModel()
        {
            var controlWithViewModelDc = this
                .AncestorsAndSelf<FrameworkElement>()
                .FirstOrDefault(c => c.DataContext is ViewModelBase);
            return controlWithViewModelDc == null
                ? null
                : (ViewModelBase)controlWithViewModelDc.DataContext;
        }

        void InvokeControllerAction(string controllerAction)
        {
            var viewModel = GetViewModel();
            if (viewModel == null) 
                return;

            if (controllerAction == null)
            {
                //If we have no initial action, we need to register ourselves anyway so we can be targeted by name
                viewModel.Context.ContentFrame.RegisterChild(this);
            }
            else
            {
                var fallbackAssembly = viewModel.Context.ControllerAction != null
                                              ? viewModel.Context.ControllerAction.Assembly
                                              : null;
                viewModel.InvokeActionBuilder
                    .InvokeAction(this, Framework.ControllerAction.CreateFrom(controllerAction, fallbackAssembly));
            }
        }

        #region public bool ControllerAction
        public static readonly DependencyProperty ControllerActionProperty =
            DependencyProperty.Register("ControllerAction", typeof (string), typeof (PartialView), new PropertyMetadata(ControllerActionChanged));

        public string ControllerAction
        {
            get { return (string) GetValue(ControllerActionProperty); }
            set { SetValue(ControllerActionProperty, value); }
        }
        #endregion

        #region public bool ShowProgressBar
        public static readonly DependencyProperty ShowProgressBarProperty =
            DependencyProperty.Register("ShowProgressBar", typeof(bool), typeof(PartialView), new PropertyMetadata(false));

        public bool ShowProgressBar
        {
            get { return (bool)GetValue(ShowProgressBarProperty); }
            set { SetValue(ShowProgressBarProperty, value); }
        }
        #endregion

        #region public Visibility ProgressBarVisibility
        public static readonly DependencyProperty ProgressBarVisibilityProperty =
            DependencyProperty.Register("ProgressBarVisibility", typeof(Visibility), typeof(PartialView), new PropertyMetadata(Visibility.Collapsed));

        public Visibility ProgressBarVisibility
        {
            get { return (Visibility)GetValue(ProgressBarVisibilityProperty); }
            set { SetValue(ProgressBarVisibilityProperty, value); }
        }
        #endregion

        #region public bool IsContentEnabled
        public static readonly DependencyProperty IsContentEnabledProperty =
            DependencyProperty.Register("IsContentEnabled", typeof(bool), typeof(PartialView), new PropertyMetadata(true));

        public bool IsContentEnabled
        {
            get { return (bool)GetValue(IsContentEnabledProperty); }
            set { SetValue(IsContentEnabledProperty, value); }
        }
        #endregion

        #region public double ContentOpacity
        public static readonly DependencyProperty ContentOpacityProperty =
            DependencyProperty.Register("ContentOpacity", typeof(double), typeof(PartialView), new PropertyMetadata(1d));

        public double ContentOpacity
        {
            get { return (double)GetValue(ContentOpacityProperty); }
            set { SetValue(ContentOpacityProperty, value); }
        }
        #endregion

        #region public string LoadingMessage
        public static readonly DependencyProperty LoadingMessageProperty =
            DependencyProperty.Register("LoadingMessage", typeof(string), typeof(PartialView), new PropertyMetadata(string.Empty));

        public string LoadingMessage
        {
            get { return (string)GetValue(LoadingMessageProperty); }
            set { SetValue(LoadingMessageProperty, value); }
        }
        #endregion

        static void ControllerActionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var partialView = (PartialView) d;
            if (partialView.hasLoaded)
                partialView.InvokeControllerAction((string)d.GetValue(ControllerActionProperty));
        }

        public void ProgressIndicatorVisible(bool isVisible)
        {
            setProgressIndicatorVisible(isVisible);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            progressIndicator = GetTemplateChild("PART_ProgressIndicator") as ProgressBar;
            setProgressIndicatorVisible = b =>
            {
                if (b)
                {
                    progressIndicator.IsIndeterminate = true;
                    progressIndicator.Visibility = Visibility.Visible;
                }
                else
                {
                    progressIndicator.IsIndeterminate = false;
                    progressIndicator.Visibility = Visibility.Collapsed;
                }
            };
        }

        public NavigationRequest CurrentRequest
        {
            get { return contentFrameController.CurrentRequest; }
        }

        public virtual void NavigateTo(NavigationRequest navigationRequest)
        {
            contentFrameController.NavigateTo(navigationRequest);
        }

        public void BeginOperation(InvokeActionRequest invokeRequest)
        {
            contentFrameController.BeginOperation(invokeRequest);
        }

        public void EndOperation(InvokeActionRequest invokeRequest)
        {
            contentFrameController.EndOperation(invokeRequest);
        }

        public void RegisterChild(IContentFrame targetFrame)
        {
            contentFrameController.RegisterChild(targetFrame);
        }

        public void DeactivateCurrentRequest()
        {
            contentFrameController.DeactivateCurrentRequest();
        }

        public void InvalidateChildren()
        {
            contentFrameController.InvalidateChildren();
        }

        public ChildFrameCollection ChildFrames
        {
            get { return contentFrameController.ChildFrames; }
        }

        public IContentFrame ParentFrame
        {
            get { return contentFrameController.ParentFrame; }
        }

        public void SetParent(IContentFrame parentFrame)
        {
            contentFrameController.SetParent(parentFrame);
        }

        event NavigationCompleteEvent IContentFrame.NavigationComplete
        {
            add { contentFrameController.NavigationComplete += value; }
            remove { contentFrameController.NavigationComplete -= value; }
        }

        event EventHandler IContentFrame.ForegroundOperationStarted
        {
            add { contentFrameController.ForegroundOperationStarted += value; }
            remove { contentFrameController.ForegroundOperationStarted -= value; }
        }

        event EventHandler IContentFrame.ForegroundOperationComplete
        {
            add { contentFrameController.ForegroundOperationComplete += value; }
            remove { contentFrameController.ForegroundOperationComplete -= value; }
        }

        public void CurrentRequestClosing(RequestClosingEventArgs eventArgs)
        {
            contentFrameController.CurrentRequestClosing(eventArgs);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                contentFrameController.Dispose();
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}